﻿using Blm.PgsqlDriver.Helpers;
using Blm.Utils.Extensions;
using Language.Resource;
using Microsoft.Extensions.Localization;
using Newtonsoft.Json.Linq;
using Npgsql;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using VisionCloud.Domain.Common;
using VisionCloud.Domain.DtoModel.Ms;
using VisionCloud.Domain.Repository.Interface.Ms;
using VisionCloud.Domain.UnitOfWork;
using VisionCloud.Utility;
using VisionCloud.Utility.Extensions;
using VisionCloud.Utility.Redis;
using OperationException = VisionCloud.Domain.Common.OperationException;

namespace VisionCloud.Service.Ms
{
    public class MsTaskService
    {
        public IUnitOfWork UnitOfWork { get; set; }

        public ICSMSTaskRepository CSMSTaskRepository { get; set; }

        public IMsTaskRepository MsTaskRepository { get; set; }

        public IMsTaskPointsRepository MsTaskPointsRepository { get; set; }

        /// <summary>
        /// 本地文件访问服务URL
        /// </summary>
        private static readonly string fileServer = ConfigHelper.GetValue<string>("FileServer");

        /// <summary>
        /// 报告文件存储路径
        /// </summary>
        private static readonly string taskReportData = ConfigHelper.GetValue<string>("TaskReportData");

        /// <summary>
        /// 默认热力图存储目录名称
        /// </summary>
        public const string DefaultHotMapDir = "热力图基础数据";

        /// <summary>
        /// 分享热力图存储目录名称
        /// </summary>
        public static readonly string ShareHotMapDir = "热力图分享数据";

        public IStringLocalizer<ServiceResource> Localizer { get; set; }

        /// <summary>
        /// 获取当前用户的最新app任务
        /// </summary>
        /// <param name="deviceReportDataDto"></param>
        /// <returns></returns>
        public async Task<ApiResult> GetReportDataStatus(DeviceReportDataDto deviceReportDataDto)
        {
            if (deviceReportDataDto == null || deviceReportDataDto.TaskReportId == Guid.Empty)
            {
                throw new OperationException(AjaxCode.Success, Localizer["YB0472"].Value);
            }
            string sql = string.Format(VisionCloudGlobalContext.GetSection("sqls:sql:query_file_status"), deviceReportDataDto.TaskReportId);

            var status = await PgSqlHelper.QueryScalarAsync<string>(sql);
            return ApiResult.Success().SetData(status);
        }

        /// <summary>
        /// 批量获取报告上传状态
        /// </summary>
        /// <param name="deviceReportDataDtos"></param>
        /// <returns></returns>
        public async Task<ApiResult> BatchGetReportDataStatus(List<DeviceReportDataDto> deviceReportDataDtos)
        {
            if (deviceReportDataDtos == null || deviceReportDataDtos.Count == 0)
            {
                throw new OperationException(AjaxCode.Success, Localizer["YB0472"].Value);
            }

            string ids = string.Empty;
            foreach (var item in deviceReportDataDtos)
            {
                ids += $"'{item.TaskReportId}',";
            }
            ids = ids.Substring(0, ids.Length - 1);
            string sql = string.Format(VisionCloudGlobalContext.GetSection("sqls:sql:query_batch_file_status"), ids);
            var deviceReportDataRespones = await PgSqlHelper.QueryListAsync<DeviceReportDataRespone>(CommandType.Text, sql);
            return ApiResult.Success().SetData(deviceReportDataRespones);
        }

        /// <summary>
        /// 根据任务Id获取该任务的汇总和站点报告
        /// </summary>
        /// <param name="dto">任务对象</param>
        /// <returns></returns>
        public async Task<List<TaskReportUrlDto>> GeTaskReportUrls(MsTaskDto dto)
        {
            var para = new NpgsqlParameter("@TaskId", dto.TaskId);
            string sql = string.Format(VisionCloudGlobalContext.GetSection("sqls:sql:query_task_report_urls"));

            var info = await PgSqlHelper.QueryOneAsync<ReportFileDto>(sql, para);
            if (info == null || string.IsNullOrWhiteSpace(info?.ResultDataFilePath))
                throw new OperationException("GX00001".SLocalize());

            if (string.IsNullOrWhiteSpace(info?.ProjectName))
                throw new OperationException("YB0064".SLocalize());

            // MinIO服务中文件的路径地址
            string path = Path.Combine(info.ResultDataFilePath, info.TaskName);
            string reportPath = Path.Combine(path, "测量报告");

            if (!Directory.Exists(path) || !Directory.Exists(reportPath))
                throw new OperationException("YB0063".SLocalize());

            var reportFiles = Directory.GetFiles(reportPath, "*.pdf");
            if (reportFiles == null || reportFiles.Length == 0)
                throw new OperationException("YB0063".SLocalize());

            // 项目名称
            var taskReports = new List<TaskReportUrlDto>();
            foreach (var report in reportFiles)
            {
                var file = new FileInfo(report);
                // 新文件名规范： “黄大哥0901-2栋-1层-初测_结构工程_自检_3.pdf”
                string fileName = file.Name.Replace(file.Extension, string.Empty);
                int result = 0;
                if (fileName.Contains("_"))
                {
                    var fileNameArray = fileName.Split("_");
                    // 过滤文件名格式不符合
                    if (fileNameArray == null || fileNameArray.Length != 4)
                    {
                        continue;
                    }

                    // 汇总报告站点序号默认为0
                    if (!int.TryParse(fileNameArray[3], out result))
                    {
                        result = 0;
                    }
                }
                else
                {
                    string tempName = fileName.Replace("测量报告", string.Empty);
                    // 兼容旧文件名规范：“测量站点1测量报告.pdf”
                    if (tempName.Length <= 4 || !int.TryParse(tempName.Substring(4, tempName.Length - 4), out result))
                    {
                        result = 0;
                    }
                }

                taskReports.Add(new TaskReportUrlDto()
                {
                    ReportName = fileName,
                    PointIndex = result,
                    ProjectName = info.ProjectName,
                    Url = StatisService.ToTaskReportUrl(file.FullName),
                });
            }
            // 按站点排序
            taskReports.Sort((a, b) =>
            {
                int c = a.PointIndex - b.PointIndex;
                return c;
            });
            return taskReports;
        }

        /// <summary>
        /// 获取单个站点的报告
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> GetPointReportUrl(MsTaskPointReportDto dto)
        {
            var (taskName, basePath) = GetTaskFilePath(dto.TaskId);
            var reportDir = Directory.GetDirectories(basePath, "测量报告", SearchOption.AllDirectories).FirstOrDefault();
            if (reportDir == null)
                throw new OperationException("MsTaskServiceNoReport".SLocalize());

            var fileList = Directory.GetFiles(reportDir, $"{taskName}*_{dto.PointIndex}.pdf");
            if (fileList == null || fileList.Count() == 0)
                throw new OperationException("MsTaskServiceNoReport".SLocalize());

            var url = ToReportWebUrl(fileList.FirstOrDefault());

            return ApiResult.Success("Success").SetData(url);
        }

        /// <summary>
        /// 获取任务详情
        /// </summary>
        /// <param name="taskId">任务id</param>
        /// <returns></returns>
        public async Task<TaskSfm> GetModel(Guid taskId, string houseTypeName)
        {
            var result = await MsTaskRepository.GetModel(taskId, houseTypeName);
            if (result == null)
                throw new OperationException("GX00002".SLocalize());
            return result;
        }

        /// <summary>
        /// 获取任务详情
        /// </summary>
        /// <param name="dto">任务Id和站点index</param>
        /// <param name="hotmapDir">存储热力图的目录</param>
        /// <returns></returns>
        public async Task<MsPicDto> GetMsPicDto(PointDto dto, string hotmapDir = DefaultHotMapDir)
        {
            var (taskName, basePath) = GetTaskFilePath(dto.TaskId);
            if (string.IsNullOrEmpty(basePath))
            {
                throw new OperationException("YB0066".SLocalize());
            }
            bool isShare = hotmapDir == ShareHotMapDir;
            var taskPoint = await MsTaskPointsRepository.GetTaskPointsAsync(dto.TaskId, dto.PointIndex);
            if (taskPoint != null && !string.IsNullOrEmpty(taskPoint.PictureJson) && !isShare)
            {
                var msPicData = taskPoint.PictureJson.ToObject<MsPicDto>();
                //不包含localhost地址表明在云端已重新绘图
                if (!msPicData.planform_path.Contains("localhost"))
                    return msPicData;
            }

            (JToken tackpointjson, JToken tackpointchangejson) = await GetTaskPointJson(dto.TaskId, dto.PointIndex);
            if (tackpointjson == null)
            {
                throw new OperationException("GX00003".SLocalize());
            }

            MsPicDto msPicDto = new MsPicDto { TaskId = dto.TaskId, PointIndex = dto.PointIndex };
            Planformpath planformPath = tackpointjson.ToObject<Planformpath>();

            #region 靠尺变更信息（此部分内容已废弃，因为tackpointchangejson早已弃用，其值必为null）

            if (tackpointchangejson != null)
            {
                List<WallRulerChange> wallRulerChangeList = tackpointchangejson.ToObject<List<WallRulerChange>>();
                //使用变更后的靠尺信息更新原来的靠尺信息
                foreach (var wall in wallRulerChangeList)
                {
                    if (wall.chuizhidu != null && wall.chuizhidu.Count > 0)
                    {
                        //当前墙面原来的垂直度靠尺列表
                        var verticalFlatRuleList = planformPath.vertical_flat.Where(p => p.title == wall.title).Select(p1 => p1.Data).FirstOrDefault();
                        foreach (var rule in wall.chuizhidu)
                        {
                            var oldRule = verticalFlatRuleList.Where(p => p.title == rule.title).FirstOrDefault();
                            if (oldRule != null)
                            {
                                oldRule.value = rule.value;
                            }
                        }
                    }

                    if (wall.pingzhengdu != null && wall.pingzhengdu.Count > 0)
                    {
                        //当前墙面原来的平整度靠尺列表
                        var wallFlatRuleList = planformPath.wall_flat.Where(p => p.title == wall.title).Select(p1 => p1.Data).FirstOrDefault();
                        foreach (var rule in wall.pingzhengdu)
                        {
                            var oldRule = wallFlatRuleList.Where(p => p.title == rule.title).FirstOrDefault();
                            if (oldRule != null)
                            {
                                oldRule.value = rule.value;
                            }
                        }
                    }
                }
            }

            #endregion 靠尺变更信息（此部分内容已废弃，因为tackpointchangejson早已弃用，其值必为null）

            #region 俯视图\靠尺

            msPicDto.planform_path = ToReportWebUrl(Path.Combine(basePath, taskName, hotmapDir, $"{dto.TaskId}_{dto.PointIndex}", "planform.png"));

            #endregion 俯视图\靠尺

            GetYinYangJiaoStraight(msPicDto, Path.Combine(basePath, taskName, "阴阳角直线度", $"{dto.TaskId}_{dto.PointIndex}"));

            //俯视图的完整路径路径
            string planformFullPath = Path.Combine(basePath, taskName, hotmapDir, $"{dto.TaskId}_{dto.PointIndex}");
            if (!Directory.Exists(planformFullPath))
            {
                throw new OperationException("YB0068".SLocalize());
            }
            //热力图的完整目录路径,墙面图在俯视图的下一级目录MsPic
            string hotViewFullDir = Path.Combine(planformFullPath, "MsPic");

            msPicDto.IsRulerSplit = await CheckRulerIsSplit(planformFullPath);

            //3D图片的完整目录路径
            string threeImageFullDir = Path.Combine(basePath, taskName, "3D", $"{dto.TaskId}_{dto.PointIndex}");

            #region 俯视图同级目录的五张图

            var picFileInPalnformFullDir = Directory.GetFiles(planformFullPath);
            foreach (var file in picFileInPalnformFullDir)
            {
                string fileName = Path.GetFileNameWithoutExtension(file);
                if (fileName.Contains("bay_window_levelness"))  // 多飘窗图片
                {
                    fileName = "bay_window_levelness";
                }

                if (fileName.Contains("ceiling_levelness"))    // 多天花图片
                {
                    fileName = "ceiling_levelness";
                }
                switch (fileName)
                {
                    case "bay_depth":
                        msPicDto.bay_depth = ToReportWebUrl(file);
                        break;

                    case "ceiling_levelness":
                        msPicDto.ceiling_levelness.Add(ToReportWebUrl(file));
                        break;

                    case "floor_levelness":
                        msPicDto.floor_levelness = ToReportWebUrl(file);
                        break;

                    case "squareness":
                        msPicDto.squareness = ToReportWebUrl(file);
                        break;

                    case "yinyangjiao":
                        msPicDto.yinyangjiao = ToReportWebUrl(file);
                        break;

                    case "bay_window_levelness":
                        msPicDto.bay_window_levelness.Add(ToReportWebUrl(file));
                        break;

                    case "platfondMeasure": // 多天花
                        msPicDto.platfondMeasure.Add(ToReportWebUrl(file));
                        break;

                    case "OneMeter_floorheight":   // 一米线标高
                        msPicDto.floorheight = ToReportWebUrl(file);
                        break;

                    case "OneMeter_Netheight":  // 一米线净高
                        msPicDto.netheight = ToReportWebUrl(file);
                        break;

                    case "Netheight":  // 空间净高
                        msPicDto.netheight = ToReportWebUrl(file);
                        break;
                }
            }

            #endregion 俯视图同级目录的五张图

            if (taskPoint != null)
            {
                if (!Directory.Exists(hotViewFullDir))
                {
                    Directory.CreateDirectory(hotViewFullDir);
                }

                #region 获取PictureJson

                var wallFiles = Directory.GetFiles(hotViewFullDir);
                msPicDto.WallPicDtos = new List<WallPicDto>();
                List<string> wallList = new List<string>();
                foreach (var file in wallFiles)//获取墙面
                {
                    string fileName = Path.GetFileNameWithoutExtension(file);
                    string wallName = fileName.Split('_')[0];
                    if (!wallList.Contains(wallName))
                    {
                        wallList.Add(wallName);
                    }
                }
                foreach (var wallName in wallList)
                {
                    var wallFileList = wallFiles.Where(p => Path.GetFileNameWithoutExtension(p).Split('_')[0] == wallName).ToList();
                    foreach (var file in wallFileList)
                    {
                        WallPicDto wallPicDto = new WallPicDto();
                        wallPicDto.WallPicId = Path.GetFileNameWithoutExtension(file);
                        wallPicDto.PicPath = ToReportWebUrl(file);
                        string fileType = Path.GetFileNameWithoutExtension(file).Split('_')[1];
                        switch (fileType)
                        {
                            case "hotmap":
                                if (wallPicDto.WallPicId.IndexOf("W") == 0)
                                {
                                    wallPicDto.Code = "hotmap";
                                    wallPicDto.WallHotViewPath = ToReportWebUrl(file);
                                }
                                else if (wallPicDto.WallPicId.IndexOf("C") == 0)
                                {
                                    wallPicDto.Code = "hotmap";
                                    wallPicDto.C_hotmap = ToReportWebUrl(file);
                                }
                                else if (wallPicDto.WallPicId.IndexOf("F") == 0)
                                {
                                    wallPicDto.Code = "hotmap";
                                    wallPicDto.F_hotmap = ToReportWebUrl(file);
                                }
                                break;

                            case "flatness":
                                if (wallPicDto.WallPicId.IndexOf("W") == 0)
                                {
                                    wallPicDto.Code = "flatness";
                                    wallPicDto.WallVerticalViewPath = ToReportWebUrl(file);
                                }
                                else if (wallPicDto.WallPicId.IndexOf("C") == 0)
                                {
                                    wallPicDto.Code = "flatness";
                                    wallPicDto.C_flatness = ToReportWebUrl(file);
                                }
                                else if (wallPicDto.WallPicId.IndexOf("F") == 0)
                                {
                                    wallPicDto.Code = "flatness";
                                    wallPicDto.F_flatness = ToReportWebUrl(file);
                                }
                                break;

                            case "vertical":
                                wallPicDto.Code = "vertical";
                                wallPicDto.WallHorizontalView = ToReportWebUrl(file);
                                break;
                        }
                        msPicDto.WallPicDtos.Add(wallPicDto);
                    }
                }

                #endregion 获取PictureJson

                #region 构造图像列表

                InitImageData(msPicDto, hotViewFullDir, planformPath.wall_flat, planformPath.vertical_flat);

                #endregion 构造图像列表

                #region 获取3D图片列表

                Get3DImageData(msPicDto, threeImageFullDir);

                #endregion 获取3D图片列表
            }

            return msPicDto;
        }

        /// <summary>
        /// 检查热力图和靠尺图是否分离
        /// </summary>
        /// <param name="palnformFullDir"></param>
        /// <returns></returns>
        private async ValueTask<bool> CheckRulerIsSplit(string palnformFullDir)
        {
            //查找最新的ini文件
            var info = Directory.GetFiles(palnformFullDir, "*.ini").Select(p => new FileInfo(p)).OrderByDescending(p => p.CreationTime).FirstOrDefault();
            if (info == null || !info.Exists)
                return false;

            var text = await File.ReadAllTextAsync(info.FullName);
            var arr = text.Trim().Split(".").Select(p => p.Replace("V", "").ToInt32()).ToArray();
            if (arr.Length != 3)
                return false;

            //大于等于2.4.1版本
            return arr[0] > 2 || (arr[0] == 2 && arr[1] > 4) || (arr[0] == 2 && arr[1] == 4 && arr[2] > 0);
        }

        /// <summary>
        /// 获取阴阳角直线度
        /// </summary>
        /// <param name="msPicDto"></param>
        /// <param name="targetDir">阴阳角直线度存储目录</param>
        private void GetYinYangJiaoStraight(MsPicDto msPicDto, string targetDir)
        {
            //阴阳角直线度
            if (!Directory.Exists(targetDir))
                return;

            var yinyangstraightnessFile = Directory.GetFiles(targetDir, "*yinyangstraightness*");
            if (yinyangstraightnessFile != null && yinyangstraightnessFile.Length > 0)
            {
                msPicDto.yinyangstraight = ToReportWebUrl(yinyangstraightnessFile.FirstOrDefault());
            }
        }

        /// <summary>
        /// 获取3D相关的json和图片
        /// </summary>
        /// <param name="threeImageFullDir"></param>
        /// <returns></returns>
        private (List<string> jsonList, List<string> imageList) Get3DImageData(string threeImageFullDir, bool isReport = true)
        {
            var files = Directory.GetFiles(threeImageFullDir);
            if (files == null || files.Length == 0)
                return (null, null);

            List<string> imageList = new List<string>();
            List<string> jsonList = new List<string>();

            foreach (var f in files)
            {
                var url = isReport ? ToReportWebUrl(f) : ToExternalwallWebUrl(f);
                var extension = Path.GetExtension(f).Replace(".", "");
                if (extension == "png")
                    imageList.Add(url);
                else
                    jsonList.Add(url);
            }

            return (jsonList, imageList);
        }

        /// <summary>
        /// 获取3D相关的json和图片
        /// </summary>
        /// <param name="msPicDto"></param>
        /// <param name="threeImageFullDir"></param>
        private void Get3DImageData(MsPicDto msPicDto, string threeImageFullDir)
        {
            var (jsonList, imageList) = Get3DImageData(threeImageFullDir);

            msPicDto.ThreeImageList = imageList;
            msPicDto.ThreeJsonList = jsonList;
        }

        /// <summary>
        /// 获取3D相关的json和图片
        /// </summary>
        /// <param name="msPicDto"></param>
        /// <param name="threeImageFullDir"></param>
        private void Get3DImageData(ExternalWallDataDto msPicDto, string threeImageFullDir)
        {
            var (jsonList, imageList) = Get3DImageData(threeImageFullDir, false);

            msPicDto.ThreeImageList = imageList;
            msPicDto.ThreeJsonList = jsonList;
        }

        /// <summary>
        /// 将本地文件转化为可访问URL
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        private string ToReportWebUrl(string filePath)
        {
            var arr = filePath.Split("task-report-data");
            var path = arr.Length == 2 ? arr[1] : arr[0];
            var line = path.StartsWith("/") ? "" : "/";
            return $"{fileServer}/taskreport{line}{path}";
        }

        /// <summary>
        /// 将本地文件转化为可访问的URL(外立面)
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public static string ToExternalwallWebUrl(string filePath)
        {
            if (!File.Exists(filePath))
            {
                return string.Empty;
            }
            var arr = filePath.Split("task-result-data");
            var path = arr.Length == 2 ? arr[1] : arr[0];
            var line = path.StartsWith("/") ? "" : "/";
            return $"{fileServer}/taskresult{line}{path}";
        }

        /// <summary>
        /// 获取任务Json数据
        /// </summary>
        /// <param name="taskId">任务Id</param>
        /// <param name="pointIndex">任务站点索引</param>
        /// <returns></returns>
        private async Task<(JToken, JToken)> GetTaskPointJson(Guid taskId, int pointIndex)
        {
            string sql = $"select tackpointjson,tackpointchangejson from taskpointtable  where uuid='{taskId}' and index='{pointIndex}'";
            var result = await PgSqlHelper.QueryOneAsync<(JToken, JToken)>(sql);
            return result;
        }

        /// <summary>
        /// 查询任务存储文件的目录
        /// </summary>
        /// <param name="taskId"></param>
        /// <returns></returns>
        private (string, string) GetTaskFilePath(Guid taskId)
        {
            var info = CSMSTaskRepository.FirstOrDefault(x => x.TaskId == taskId);
            return (info?.TaskName, info?.ResultDataFilePath);
        }

        /// <summary>
        /// 构造前端格式数据
        /// </summary>
        /// <param name="msPicDto"></param>
        /// <param name="hotViewFullDir"></param>
        /// <param name="wall_flat"></param>
        /// <param name="vertical_flat"></param>
        private MsPicDto InitImageData(MsPicDto msPicDto, string hotViewFullDir, List<WallRuler> wall_flat = null, List<WallRuler> vertical_flat = null)
        {
            if (msPicDto.ImageDatas != null)
            {
                msPicDto.ImageDatas.Clear();
            }
            foreach (var item in MsPicDto.DicType)
            {
                // 类型实体
                var model = new ImageData()
                {
                    Code = item.Code,
                    ImageModels = new List<ImageModel>()
                };

                foreach (var item1 in item.ValueList)
                {
                    string tempKey = string.Empty;
                    if (item.ValueList[0].StartsWith("W_") || item.ValueList[0].StartsWith("C_") || item.ValueList[0].StartsWith("F_"))
                    {
                        tempKey = item.ValueList[0].Split("_")[0];
                    }
                    else
                    {
                        tempKey = item1;
                    }

                    model.Key = tempKey;
                    model.MeasureCodes = item.MeasureCodes;
                    var datas = msPicDto.WallPicDtos.Where(x => x.WallPicId.StartsWith(model.Key)).OrderBy(y => y.WallPicId).ToList();
                    ImageModel temp = null;
                    string itemKey = string.Empty;
                    if (datas != null && datas.Count > 0)     // 有热力图
                    {
                        foreach (var item2 in datas)
                        {
                            var arr = item2.WallPicId.Split("_");
                            string currentItemKey = arr[0];
                            string currentItemValue = arr[1];
                            if (currentItemKey.EndsWith("baseboard") || currentItemKey.EndsWith("doorline") || currentItemValue == "basemap")
                            {
                                continue;
                            }
                            if (!string.Equals(itemKey, currentItemKey))
                            {
                                string tempArea = "0";
                                if (item2.Area == "中")
                                {
                                    tempArea = "1";
                                }
                                else if (item2.Area == "高")
                                {
                                    tempArea = "2";
                                }
                                itemKey = currentItemKey;
                                var wallFlatList = wall_flat == null ? new List<RuleData>() : wall_flat.Where(p => p.title == itemKey).Select(p1 => p1.Data).FirstOrDefault();
                                var verticalFlatList = vertical_flat == null ? new List<RuleData>() : vertical_flat.Where(p => p.title == itemKey).Select(p1 => p1.Data).FirstOrDefault();
                                var rulerDatas = new List<RuleData>();
                                int i = 0;
                                if (wallFlatList != null && wallFlatList.Count > 0)
                                {
                                    foreach (var ruler in wallFlatList)
                                    {
                                        rulerDatas.Add(new RuleData()
                                        {
                                            id = Guid.NewGuid(),
                                            title = ruler.title,
                                            value = decimal.Round(ruler.value, 1),
                                            type = "平整度",
                                            flag = ruler.flag
                                        });
                                    }
                                }

                                if (verticalFlatList != null && verticalFlatList.Count > 0)
                                {
                                    foreach (var ruler in verticalFlatList)
                                    {
                                        rulerDatas.Add(new RuleData()
                                        {
                                            id = Guid.NewGuid(),
                                            title = ruler.title,
                                            value = decimal.Round(ruler.value, 1),
                                            type = "垂直度",
                                            flag = ruler.flag
                                        });
                                    }
                                }

                                temp = new ImageModel()
                                {
                                    Code = item.Code,
                                    Id = item2.WallPicId,
                                    GrindingAmount = item2.GrindingAmount,
                                    Area = tempArea,
                                    ShowVertical = item2.ShowVertical,
                                    ShowFlatness = item2.ShowHorizontal,

                                    // 默认值选中垂直的和平整度标准尺,不允许配置其他靠尺
                                    FlatnessRuler = true,
                                    VerticalRuler = true,
                                    StandardFlatnessRuler = true,
                                    StandardVerticalRuler = true,
                                    FullFlatnessValidRuler = false,
                                    FullFlatnessInvalidRuler = false,
                                    VerticalValidRuler = false,
                                    VerticalInvalidRuler = false,
                                    RulerDatas = rulerDatas,
                                };
                            }

                            if (temp == null)
                            {
                                continue;
                            }
                            if (string.Equals(item2.Code, "hotmap"))
                            {
                                // 热量图URL
                                temp.HotmapUrl = item2.PicPath;
                                // 靠尺图
                                temp.RulesUrl = item2.PicPath.Replace("hotmap", "ruler");
                                if (msPicDto.IsRulerSplit)
                                {
                                    var id = item2.WallPicId.Replace("hotmap", "basemap");
                                    temp.BasemapUrl = datas.FirstOrDefault(p => p.WallPicId == id)?.PicPath;
                                }
                                if (!string.IsNullOrWhiteSpace(temp.HotmapUrl))
                                {
                                    model.ImageModels.Add(temp);
                                }
                            }
                            if (string.Equals(item2.Code, "flatness"))
                            {
                                // 平整度URL
                                temp.FlatnessUrl = item2.PicPath;
                            }
                            if (string.Equals(item2.Code, "vertical"))
                            {
                                // 垂直度URL
                                temp.VerticalUrl = item2.PicPath;
                            }
                        }
                    }
                    else
                    {
                        if (tempKey.Equals("bay_window_levelness", StringComparison.OrdinalIgnoreCase))
                        {
                            foreach (var item3 in msPicDto.bay_window_levelness)
                            {
                                temp = new ImageModel()
                                {
                                    Code = item.Code,
                                    Id = tempKey,
                                    HotmapUrl = item3,
                                };

                                if (!string.IsNullOrWhiteSpace(item3))
                                {
                                    model.ImageModels.Add(temp);
                                }
                            }
                        }
                        else if (tempKey.Equals("ceiling_levelness", StringComparison.OrdinalIgnoreCase))
                        {
                            foreach (var item3 in msPicDto.ceiling_levelness)
                            {
                                temp = new ImageModel()
                                {
                                    Code = item.Code,
                                    Id = tempKey,
                                    HotmapUrl = item3,
                                };

                                if (!string.IsNullOrWhiteSpace(item3))
                                {
                                    model.ImageModels.Add(temp);
                                }
                            }
                        }
                        else if (tempKey.Equals("baseboard", StringComparison.OrdinalIgnoreCase)
                            || tempKey.Equals("doorline", StringComparison.OrdinalIgnoreCase))
                        {
                            //踢脚线只有平整度
                            var baseboard = tempKey.Equals("baseboard");
                            var showVertical = !baseboard;
                            GetDirectoryImageDatas(msPicDto.WallPicDtos, tempKey, model, hotViewFullDir, true, showVertical);
                        }
                        else if (tempKey.Equals("hole", StringComparison.OrdinalIgnoreCase))
                        {
                            // 门洞尺寸
                            var path = Directory.GetParent(hotViewFullDir);
                            GetImages(model, path.FullName, "hole");
                        }
                        else if (tempKey.Equals("netheight", StringComparison.OrdinalIgnoreCase)
                            || tempKey.Equals("wirebox", StringComparison.OrdinalIgnoreCase)
                            || tempKey.Equals("airconditionhole", StringComparison.OrdinalIgnoreCase))
                        {
                            // V5.2hotfix版本中净高测量项会输出2张图片，一米线净高和空间净高；
                            // 后期如果测量项分为“一米线净高”和“空间净高”后可以将下面逻辑删除
                            var path = Directory.GetParent(hotViewFullDir);
                            GetImages(model, path.FullName, string.Empty, tempKey);
                        }
                        else
                        {
                            var url = GetModelValue(tempKey, msPicDto);
                            temp = new ImageModel()
                            {
                                Code = item.Code,
                                Id = tempKey,
                                HotmapUrl = url,
                            };

                            if (!string.IsNullOrWhiteSpace(url))
                            {
                                model.ImageModels.Add(temp);
                            }
                        }
                    }
                }
                msPicDto.ImageDatas.Add(model);
            }
            return msPicDto;
        }

        /// <summary>
        /// 获取某个文件夹的ImageData
        /// </summary>
        /// <param name="wallPicDtos"></param>
        /// <param name="searchPattern">文件名包含的字符</param>
        /// <param name="imageData"></param>
        /// <param name="hotViewFullDir">热力图目录</param>
        /// <param name="showFlatness">是否显示水平度</param>
        /// <param name="showVertical">是否显示垂直度</param>
        private void GetDirectoryImageDatas(List<WallPicDto> wallPicDtos, string searchPattern, ImageData imageData, string hotViewFullDir, bool showFlatness = false, bool showVertical = false)
        {
            if (!Directory.Exists(hotViewFullDir))
                return;

            var files = Directory.GetFiles(hotViewFullDir, $"*{searchPattern}*");
            if (files.Length == 0)
                return;

            List<string> wallList = new List<string>();
            foreach (var fl in files)
            {
                var wl = fl.Split(searchPattern)[0];
                if (!wallList.Contains(wl))
                    wallList.Add(wl);
            }

            if (wallList.Count > 0)
            {
                wallList = wallList.OrderBy(p => p).ToList();
                foreach (var item in wallList)
                {
                    var file = files.Where(p => p.StartsWith(item) && p.Contains("hotmap")).FirstOrDefault();
                    // 找门套现和踢脚线墙面图
                    var baseMapFile = files.Where(p => p.StartsWith(item) && p.Contains(searchPattern) && p.Contains("basemap")).FirstOrDefault();
                    var rulerFile = files.Where(p => p.StartsWith(item) && p.Contains(searchPattern) && p.Contains("ruler")).FirstOrDefault();
                    if (File.Exists(file))
                    {
                        var model = new ImageModel
                        {
                            Id = Path.GetFileNameWithoutExtension(file),
                            Code = imageData.Code,
                            HotmapUrl = ToReportWebUrl(file),
                            RulesUrl = ToReportWebUrl(rulerFile),
                            ShowFlatness = showFlatness,
                            ShowVertical = showVertical,
                            BasemapUrl = File.Exists(baseMapFile) ? ToReportWebUrl(baseMapFile) : null
                        };
                        var pic = wallPicDtos.FirstOrDefault(p => p.WallPicId == model.Id);
                        if (pic != null)
                        {
                            model.GrindingAmount = pic.GrindingAmount;
                            model.ShowVertical = pic.ShowVertical;
                            model.ShowFlatness = pic.ShowHorizontal;

                            // 默认值选中垂直的和平整度标准尺,不允许配置其他靠尺
                            model.FlatnessRuler = true;
                            model.VerticalRuler = true;
                            model.StandardFlatnessRuler = true;
                            model.StandardVerticalRuler = true;
                            model.FullFlatnessValidRuler = false;
                            model.FullFlatnessInvalidRuler = false;
                            model.VerticalValidRuler = false;
                            model.VerticalInvalidRuler = false;
                        }

                        imageData.ImageModels.Add(model);
                    }
                }
            }
        }

        /// <summary>
        /// 获取文件夹中图片
        /// </summary>
        /// <param name="model"></param>
        /// <param name="palnformFullDir"></param>
        /// <param name="folderName"></param>
        /// <param name="searchPattern">文件关匹配键字</param>
        private void GetImages(ImageData model, string palnformFullDir, string folderName, string searchPattern = "")
        {
            // 对应文件目录如：hole
            var imageDir = Path.Combine(palnformFullDir, folderName);
            if (!Directory.Exists(imageDir))
                Directory.CreateDirectory(imageDir);
            var imageFiles = Directory.GetFiles(imageDir, "*.png");
            // 根据名称过滤
            if (!string.IsNullOrEmpty(searchPattern))
            {
                imageFiles = imageFiles.Where(x => x.Contains(searchPattern, StringComparison.OrdinalIgnoreCase)).ToArray();
            }
            if (imageFiles != null && imageFiles.Length > 0)
            {
                foreach (var item in imageFiles)
                {
                    var temp = new ImageModel()
                    {
                        Code = string.Empty,
                        Id = string.Empty,
                        HotmapUrl = ToReportWebUrl(item),
                    };
                    model.ImageModels.Add(temp);
                }
            }
        }

        /// <summary>
        /// 根据字段名称获取对应值
        /// </summary>
        /// <param name="FieldName"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        private string GetModelValue(string FieldName, object obj)
        {
            try
            {
                if (string.IsNullOrEmpty(FieldName) || obj == null)
                    return null;
                Type Ts = obj.GetType();
                var pi = Ts.GetProperty(FieldName);
                if (pi != null)
                {
                    object o = pi.GetValue(obj, null);
                    if (o != null)
                    {
                        string Value = Convert.ToString(o);
                        if (string.IsNullOrEmpty(Value)) return null;
                        return Value;
                    }
                }
                return null;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 调用算法重新生成图片
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> RedrawPicture(RedrawDto dto)
        {
            if (dto.PicturePath.Contains("ruler"))
            {
                dto.PicturePath = dto.PicturePath.Replace("ruler", "hotmap");
            }

            var filePath = GetPicturePath(dto.PicturePath);
            dto.WallPicId = Path.GetFileNameWithoutExtension(filePath);
            bool flag = false;

            var lockKey = $"redrawpicture_{dto.TaskId}_{dto.PointIndex}";
            if (await RedisHelper.Instance.LockTakeAsync(lockKey, Environment.MachineName, TimeSpan.FromSeconds(3)))
            {
                try
                {
                    flag = MeasureDrawHotMapService.MeasureDrawHotMap_R(filePath, (float)dto.GrindingAmount, Convert.ToInt32(dto.Area));
                }
                catch (Exception ex)
                {
                    Logger.Error(ex.Message, ex);
                    return ApiResult.SetCode(AjaxCode.Fail, ex.Message);
                }
                finally
                {
                    await RedisHelper.Instance.LockReleaseAsync(lockKey, Environment.MachineName);
                }
            }

            if (!flag)
            {
                return ApiResult.SetCode(AjaxCode.Fail, "GX00004".SLocalize());
            }

            PointDto pointDto = new PointDto() { TaskId = dto.TaskId, PointIndex = dto.PointIndex };
            var msPicDto = await GetMsPicDto(pointDto);
            var taskPoint = await MsTaskPointsRepository.GetTaskPointsAsync(pointDto.TaskId, pointDto.PointIndex);
            var wallDto = msPicDto.WallPicDtos.Where(p => p.WallPicId == dto.WallPicId).FirstOrDefault();
            if (wallDto != null)
            {
                wallDto.GrindingAmount = dto.GrindingAmount;
                wallDto.Area = dto.Area.ToString();
            }

            foreach (var item in msPicDto.ImageDatas)
            {
                var imgModel = item.ImageModels.Where(p => p.Id == dto.WallPicId).FirstOrDefault();
                if (imgModel != null)
                {
                    imgModel.GrindingAmount = dto.GrindingAmount;
                    imgModel.Area = Convert.ToInt32(dto.Area).ToString();
                    break;
                }
            }

            taskPoint.PictureJson = msPicDto.ToJsonStr();
            MsTaskPointsRepository.Update(taskPoint);
            await UnitOfWork.SaveChangesAsync();

            return ApiResult.Success();
        }

        /// <summary>
        /// 生成靠尺图
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ApiResult> GenAlgorithmImage(AlgorithmSettingDto dto)
        {
            if (dto.PicturePath.Contains("hotmap"))
            {
                dto.PicturePath = dto.PicturePath.Replace("hotmap", "ruler");
            }
            var filePath = GetPicturePath(dto.PicturePath);
            ApiResult apiResultDto = new ApiResult(AjaxCode.Success);
            bool has_flatness = false, has_vertical = false;
            bool flag = false;

            var lockKey = $"genalgorithmimage_{dto.TaskId}_{dto.PointIndex}";
            if (await RedisHelper.Instance.LockTakeAsync(lockKey, Environment.MachineName, TimeSpan.FromSeconds(3)))
            {
                try
                {
                    flag = MeasureDrawHotMapService.MeasureDrawRulers_R(filePath, dto.FlatnessRuler, dto.VerticalRuler, ref has_flatness, ref has_vertical);
                }
                catch (Exception ex)
                {
                    Logger.Error(ex.Message, ex);
                    apiResultDto.Code = AjaxCode.Fail;
                    apiResultDto.Message = ex.Message;
                    return apiResultDto;
                }
                finally
                {
                    await RedisHelper.Instance.LockReleaseAsync(lockKey, Environment.MachineName);
                }
            }

            if (!flag)
            {
                apiResultDto.Code = AjaxCode.Fail;
                apiResultDto.Message = "GX00006".SLocalize();
                return apiResultDto;
            }

            PointDto pointDto = new PointDto() { TaskId = dto.TaskId, PointIndex = dto.PointIndex };
            var msPicDto = await GetMsPicDto(pointDto);
            var taskPoint = await MsTaskPointsRepository.GetTaskPointsAsync(pointDto.TaskId, pointDto.PointIndex);
            var temp = msPicDto.WallPicDtos.FirstOrDefault(p => p.WallPicId == dto.WallPicId);

            foreach (var item in msPicDto.ImageDatas)
            {
                var temp2 = item.ImageModels.FirstOrDefault(p => p.Id == dto.WallPicId);
                if (temp2 != null)
                {
                    temp2.FlatnessRuler = dto.FlatnessRuler;
                    temp2.VerticalRuler = dto.VerticalRuler;
                    temp2.RulesUrl = ToReportWebUrl(filePath);
                    temp2.ShowFlatness = has_flatness;
                    temp2.ShowVertical = has_vertical;
                    break;
                }
            }

            taskPoint.PictureJson = msPicDto.ToJsonStr();
            MsTaskPointsRepository.Update(taskPoint);  // 更新数据库JSON字段
            await UnitOfWork.SaveChangesAsync();

            return apiResultDto;
        }

        /// <summary>
        /// 获取图片在服务器存放的目录
        /// </summary>
        /// <param name="pictureUrl"></param>
        /// <returns></returns>
        private string GetPicturePath(string pictureUrl)
        {
            var pathArr = pictureUrl.Split("taskreport");
            var file = pathArr.Length == 2 ? pathArr[1] : pathArr[0];
            var doc = file.StartsWith("/") ? file.Substring(1) : file;
            var path = Path.Combine(taskReportData, doc);
            if (!File.Exists(path))
                throw new OperationException("GX00005".SLocalize());

            return path;
        }

        /// <summary>
        /// 获取外立面的墙面信息
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        /// <exception cref="OperationException"></exception>
        public async Task<ExternalWallDataDto> GetExternalWallDataDetail(PointDto dto)
        {
            var (_, basePath) = GetTaskFilePath(dto.TaskId);
            if (string.IsNullOrEmpty(basePath))
                throw new OperationException("YB0066".SLocalize());

            var taskPoint = await MsTaskPointsRepository.GetTaskPointsAsync(dto.TaskId, dto.PointIndex);
            if (taskPoint == null)
                throw new OperationException("TaskPointNoExists".SLocalize());

            if (!string.IsNullOrEmpty(taskPoint.PictureJson))
            {
                var msPicData = taskPoint.PictureJson.ToObject<ExternalWallDataDto>();
                //IsNewest = true表明在云端已重新构建最新的PictureJson
                if (msPicData.IsNewest)
                    return msPicData;
            }

            (JToken tackpointjson, _) = await GetTaskPointJson(dto.TaskId, dto.PointIndex);
            if (tackpointjson == null)
                throw new OperationException("GX00003".SLocalize());

            //站点数据存储目录
            string basePointDir = Path.Combine(basePath, $"{dto.PointIndex}");
            if (!Directory.Exists(basePointDir))
                throw new OperationException("YB0066".SLocalize());

            ExternalWallDataDto externalWallData = new ExternalWallDataDto
            {
                ReportFilePath = taskPoint.filePath,
                TaskId = dto.TaskId,
                PointIndex = dto.PointIndex,
                IsNewest = true,
                // 轮廓图URL
                ContourImagePath = ToExternalwallWebUrl(Path.Combine(basePointDir, "contour.png"))
            };
            var imageData = new WallImageData { Code = "外立面", Key = "externalwall" };

            //子站点报告
            var reportFile = Directory.GetFiles(basePointDir, $"测量站点{dto.PointIndex}外立面测量报告.pdf").FirstOrDefault();
            if (File.Exists(reportFile))
                externalWallData.ReportFilePath = ToExternalwallWebUrl(reportFile);

            //3D图片的完整目录路径
            string threeImageFullDir = Path.Combine(basePointDir, "3D");

            //热力图的完整目录路径
            var hotmapImageFullDir = Path.Combine(basePointDir, "baseMap");

            //分层图片的完整目录路径
            var floorImageFullDir = Path.Combine(basePointDir, "SegFloorImg");

            //加载墙面列表
            imageData.ImageModels = LoadWallImageModels(hotmapImageFullDir);

            //加载楼层列表
            LoadFloorImageModels(floorImageFullDir, imageData.ImageModels);

            // 获取3D相关的json和图片
            Get3DImageData(externalWallData, threeImageFullDir);

            externalWallData.ImageDatas.Add(imageData);

            //更新PictureJson
            taskPoint.PictureJson = externalWallData.ToJsonStr();
            MsTaskPointsRepository.Update(taskPoint);
            await UnitOfWork.SaveChangesAsync();

            return externalWallData;
        }

        /// <summary>
        /// 加载目录下的墙面图片列表
        /// </summary>
        /// <param name="folderPath"></param>
        /// <returns></returns>
        private static List<WallImageModel> LoadWallImageModels(string folderPath)
        {
            var wallImageModels = new List<WallImageModel>();
            if (Directory.Exists(folderPath))
            {
                var fileList = Directory.GetFiles(folderPath, "*.png").ToList();

                var wallIds = fileList
                    .Select(file => Path.GetFileNameWithoutExtension(file).Split('_')[0])
                    .Distinct()
                    .ToList();

                foreach (var wallId in wallIds)
                {
                    var wallImageModel = new WallImageModel
                    {
                        Id = wallId,
                        BasemapUrl = ToExternalwallWebUrl(fileList.FirstOrDefault(file => file.Contains($"{wallId}_baseMap.png"))),
                        HotmapUrl = ToExternalwallWebUrl(fileList.FirstOrDefault(file => file.Contains($"{wallId}_hotmap.png"))),
                        RulesUrl = ToExternalwallWebUrl(fileList.FirstOrDefault(file => file.Contains($"{wallId}_ruler.png")))
                    };

                    wallImageModels.Add(wallImageModel);
                }
            }

            return wallImageModels.OrderBy(p => p.Id, new BlmStringComparer()).ToList();
        }

        /// <summary>
        /// 加载楼层图片列表
        /// </summary>
        /// <param name="folderPath"></param>
        /// <param name="wallImageModels"></param>
        private static void LoadFloorImageModels(string folderPath, List<WallImageModel> wallImageModels)
        {
            if (Directory.Exists(folderPath))
            {
                var fileList = Directory.GetFiles(folderPath, "*.png").ToList();

                foreach (var wallImageModel in wallImageModels)
                {
                    var floors = fileList
                        .Where(file => Path.GetFileName(file).StartsWith(wallImageModel.Id))
                        .Select(file => Path.GetFileNameWithoutExtension(file).Split('_')[1])
                        .Distinct()
                        .ToList();

                    var baseFile = fileList.FirstOrDefault();
                    var fileName = Path.GetFileName(baseFile);
                    var floorUrl = ToExternalwallWebUrl(baseFile);

                    wallImageModel.FloorUrl = floorUrl.Split(fileName)[0];
                    foreach (var floor in floors)
                    {
                        var floorImageModel = new FloorImageModel
                        {
                            Floor = floor,
                            BasemapPath = Path.GetFileName(fileList.FirstOrDefault(file => file.Contains($"{wallImageModel.Id}_{floor}_baseMap.png"))),
                            HotmapPath = Path.GetFileName(fileList.FirstOrDefault(file => file.Contains($"{wallImageModel.Id}_{floor}_hotmap.png"))),
                            RulesPath = Path.GetFileName(fileList.FirstOrDefault(file => file.Contains($"{wallImageModel.Id}_{floor}_ruler.png")))
                        };
                        wallImageModel.FloorModels.Add(floorImageModel);
                    }
                    wallImageModel.FloorModels = wallImageModel.FloorModels.OrderBy(p => p.Floor, new BlmStringComparer()).ToList();
                }
            }
        }
    }
}